/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import org.zhiqim.httpd.entities.ClassResourceEntity;
import org.zhiqim.httpd.entities.FileResourceEntity;

import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Resources;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;

/**
 * HTTP资源，包括资源索引，资源类型和资源路径
 * 
 * 以便HttpContext支持多个资源目录，从而实现资源组件化
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class HttpResource implements HttpdConstants
{
    public static final HttpResourceCompor _resourceCompor = new HttpResourceCompor();
    public static final FileResourceEntity _fileResource = new FileResourceEntity();
    public static final ClassResourceEntity _classResource = new ClassResourceEntity();
    
    private int resourceIndex;
    private String resourceType;//classpath|directory
    private String resourcePath;
    
    public HttpResource(String resourceType, String resourcePath)
    {
        this.resourceType = resourceType;
        this.resourcePath = Strings.removeEndsWith(resourcePath, "/");
    }
    
    public HttpResource(int resourceIndex, String resourceType, String resourcePath)
    {
        this.resourceIndex = resourceIndex;
        this.resourceType = resourceType;
        this.resourcePath = Strings.removeEndsWith(resourcePath, "/");
    }
    
    public boolean isValid()
    {
        return Validates.isNotEmpty(resourcePath) && (_CLASSPATH_.equals(resourceType) || _DIRECTORY_.equals(resourceType));
    }
    
    /********************************************************************/
    //业务处理等方法
    /********************************************************************/
    
    /**
     * 在该资源内处理请求
     * 
     * @param header        请求头
     * @param sender        响应发送器
     * @return              =true表示处理到，=false表示不在该资源内
     * @throws IOException  异常
     */
    public boolean handleResource(HttpHeader header, HttpSender sender) throws IOException
    {
        //1.如果未配置资源直接返回false
        if (resourceType == null || resourcePath == null)
            return false;
        
        //2.先查找资源是否存在
        String path = resourcePath + header.getPathInContext();
        path = URLDecoder.decode(path, header.getCharacterEncoding());
        
        if (isClasspath())
        {//classpath
            if (!Resources.exists(HttpResource.class, path))
                return false;
            
            if (header.isMethodHead())
            {//如果HEAD，直接返回成功
                sender.sendHeader(_200_OK_);
                return true;
            }
            
            ((HttpHeaderInner)header).setPathOnResource(path);
            _classResource.handle(header, sender);
            return true;
        }
        else
        {//dir
            if (!Files.isFile(path))
                return false;
            
            if (header.isMethodHead())
            {//如果HEAD，直接返回成功
                sender.sendHeader(_200_OK_);
                return true;
            }
            
            ((HttpHeaderInner)header).setPathOnResource(path);
            _fileResource.handle(header, sender);
            return true;
        }
    }
    
    /**
     * 获取该资源内的资源字符串
     * 
     * @param path          请求
     * @return              =null表示不在该资源内,!=null表示在该资源内并读取到内容
     * @throws IOException  异常
     */
    public String getResourceString(String path) throws IOException
    {
        return getResourceString(path, _UTF_8_);
    }
    
    /**
     * 获取该资源内的资源字符串
     * 
     * @param path          请求
     * @param encoding      编码
     * @return              =null表示不在该资源内,!=null表示在该资源内并读取到内容
     * @throws IOException  异常
     */
    public String getResourceString(String path, String encoding) throws IOException
    {
        if (resourcePath == null)
            return null;
        
        path = resourcePath + "/" + Strings.removeStartsWith(path, "/");
        if (isClasspath())
        {
            if (!Resources.exists(HttpResource.class, path))
                return null;
            
            return Resources.getResourceString(HttpResource.class, path, encoding);
        }
        else
        {
            if (!Files.isFile(path))
                return null;
            
            return Files.read(path, encoding);
        }
    }
    
    /**
     * 读取资源目录下的文件名和目录名，目录名以/结尾，用于显示目录下列表
     * 
     * @param folderPath    目录地址
     * @return              名称列表
     * @throws IOException  异常
     */
    public List<String> getResourceNameList(String folderPath) throws IOException
    {
        if (resourcePath == null)
            return new ArrayList<>(0);
            
        folderPath = resourcePath + "/" + Strings.removeStartsWith(folderPath, "/");
        if (isClasspath())
        {
            if (!Resources.exists(HttpResource.class, folderPath))
                return new ArrayList<>(0);
            
            return Resources.getResourceNameList(HttpResource.class, folderPath);
        }
        else
        {
            if (!Files.isDirectory(folderPath))
                return new ArrayList<>(0);
            
            File folder = new File(folderPath);
            File[] files = folder.listFiles(new FileFilter(){public boolean accept(File file){return !file.isHidden();}});
            if (files == null)
                return new ArrayList<>(0);
                
            List<String> nameList = new ArrayList<>();
            for (File file : files)
            {
                if (file.isDirectory())
                    nameList.add(file.getName()+"/");
                else
                    nameList.add(file.getName());
            }
            
            return nameList;
        }
    }
    
    /********************************************************************/
    //属性 get & set 等方法
    /********************************************************************/
    
    public boolean isClasspath()
    {
        return _CLASSPATH_.equals(resourceType);
    }
    
    public int getResourceIndex()
    {
        return resourceIndex;
    }
    public void setResourceIndex(int resourceIndex)
    {
        this.resourceIndex = resourceIndex;
    }
    public String getResourceType()
    {
        return resourceType;
    }
    public void setResourceType(String resourceType)
    {
        this.resourceType = resourceType;
    }
    public String getResourcePath()
    {
        return resourcePath;
    }
    public void setResourcePath(String resourcePath)
    {
        this.resourcePath = resourcePath;
    }

    /********************************************************************/
    //定义HTTP资源等resourcePath & resourceType都相等时即对象相等
    /********************************************************************/
    
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((resourcePath == null) ? 0 : resourcePath.hashCode());
        result = prime * result + ((resourceType == null) ? 0 : resourceType.hashCode());
        return result;
    }

    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        HttpResource other = (HttpResource) obj;
        if (resourcePath == null)
        {
            if (other.resourcePath != null)
                return false;
        }
        else if (!resourcePath.equals(other.resourcePath))
            return false;
        if (resourceType == null)
        {
            if (other.resourceType != null)
                return false;
        }
        else if (!resourceType.equals(other.resourceType))
            return false;
        return true;
    }
    
    /** 按索引排序 */
    private static class HttpResourceCompor implements Comparator<HttpResource>
    {
        public int compare(HttpResource o1, HttpResource o2)
        {
            return o1.getResourceIndex() - o2.getResourceIndex();
        }
        
    }
}
